package com.misyi.framework.web.okhttp;


import com.misyi.framework.api.SystemException;
import okhttp3.OkHttpClient;

import javax.net.ssl.*;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * OkHttp客户端工厂类
 *
 * @author wuwen
 * @date 2018-03-26 11:35 AM
 */
public class OkHttpClientFactory {

    /**
     * 虽然支持HTTPS，但是默认不会加载证书
     * connectTimeout默认值：10s
     * writeTimeout默认值: 10s
     * readTimeout默认值: 10s
     * @return
     */
    public static OkHttpClient getOkHttpClient(){
        TrustManager[] trustManagers = getTrustManagers();
        return new OkHttpClient.Builder()
                .sslSocketFactory(getSSLSocketFactory(trustManagers), (X509TrustManager) trustManagers[0])
                .hostnameVerifier(getHostnameVerifier())
                .build();
    }

    /**
     * 获取指定连接、写入、读取超时时间的OkHttpClient
     * @param connectTimeoutMillis 连接超时毫秒数
     * @param writeTimeoutMillis 写入超时毫秒数
     * @param readTimeoutMillis 读取超时毫秒数
     * @return
     */
    public static OkHttpClient getOkHttpClient(long connectTimeoutMillis,
                                               long writeTimeoutMillis,
                                               long readTimeoutMillis){
        TrustManager[] trustManagers = getTrustManagers();
        return new OkHttpClient.Builder()
                .connectTimeout(connectTimeoutMillis, TimeUnit.MILLISECONDS)
                .writeTimeout(writeTimeoutMillis, TimeUnit.MILLISECONDS)
                .readTimeout(readTimeoutMillis, TimeUnit.MILLISECONDS)
                .sslSocketFactory(getSSLSocketFactory(trustManagers), (X509TrustManager) trustManagers[0])
                .hostnameVerifier(getHostnameVerifier())
                .build();
    }

    /**
     * 根据证书路径获取会校验服务端根证书的OkHttpClient
     * @param certificatePath 证书路径
     * @return
     */
    public static OkHttpClient getOkHttpClient(String certificatePath){
        TrustManager[] trustManagers = getTrustManagers(certificatePath);
        return new OkHttpClient.Builder()
                .sslSocketFactory(getSSLSocketFactory(trustManagers), (X509TrustManager) trustManagers[0])
                .build();
    }

    /**
     * 根据证书路径获取会校验服务端根证书的OkHttpClient
     * @param connectTimeoutMillis 连接超时毫秒数
     * @param writeTimeoutMillis 写入超时毫秒数
     * @param readTimeoutMillis 读取超时毫秒数
     * @param certificatePath 证书路径
     * @return
     */
    public static OkHttpClient getOkHttpClient(long connectTimeoutMillis,
                                               long writeTimeoutMillis,
                                               long readTimeoutMillis,
                                               String certificatePath){
        TrustManager[] trustManagers = getTrustManagers(certificatePath);
        return new OkHttpClient.Builder()
                .connectTimeout(connectTimeoutMillis, TimeUnit.MILLISECONDS)
                .writeTimeout(writeTimeoutMillis, TimeUnit.MILLISECONDS)
                .readTimeout(readTimeoutMillis, TimeUnit.MILLISECONDS)
                .sslSocketFactory(getSSLSocketFactory(trustManagers), (X509TrustManager) trustManagers[0])
                .build();
    }

    /**
     * 根据证书路径获取证书信任管理器
     * @param certificatePath
     * @return
     */
    private static TrustManager[] getTrustManagers(String certificatePath) {
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);
            String certificateAlias = Integer.toString(0);
            Certificate certificate = certificateFactory.generateCertificate(OkHttpClientFactory.class.getResourceAsStream(certificatePath));
            keyStore.setCertificateEntry(certificateAlias, certificate);
            final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
            }
            return trustManagers;
        } catch (Exception e) {
            throw new SystemException("加载证书出现异常：" + e.getMessage(), e);
        }
    }

    /**
     * 获取忽略证书的信任管理器
     * @return
     */
    private static TrustManager[] getTrustManagers() {
        return new TrustManager[]{new X509TrustManager(){

            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }};
    }

    /**
     * 获取SSL套接字工厂
     * @param trustManagers
     * @return
     */
    private static SSLSocketFactory getSSLSocketFactory(TrustManager[] trustManagers){
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustManagers, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception e) {
            throw new SystemException("创建SSLSocketFactory出现异常：" + e.getMessage(), e);
        }
    }

    /**
     * 获取忽略主机名验证器
     * @return
     */
    private static HostnameVerifier getHostnameVerifier(){
        return (String hostname, SSLSession sslSession) -> true;
    }

}
